package com.hundsun.cprs.yyt.service.match.core.queue;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import com.hundsun.cprs.yyt.service.match.core.queue.visitor.QueueVisitor;
import com.hundsun.cprs.yyt.service.match.domain.TradeEntrust;

/**
 * 时间优先的订单组合队列
 * 用于对同一个方向的队列切分为两个队列，通过权重来优先匹配订单处理
 * @author linrl
 * @date 2019年3月2日
 */
public class TimePriorityCompositeQueue extends PriceQueue {
    
    private TimePriorityQueue oldUserQueue = new TimePriorityQueue();//老用户队列
    private TimePriorityQueue newUserQueue = new TimePriorityQueue();//新用户队列
    private List<QueueItem> queueItems = new ArrayList<QueueItem>();
    private int currentIndex;  
    private int totalQueue;
    private int currentWeight;  
    private int maxWeight;  
    private int gcdWeight;
    
    private int newUserWeight;
    private int oldUserWeight;
    
    private ReadWriteLock resetLock = new ReentrantReadWriteLock();
    
    public TimePriorityCompositeQueue(int newWeight,int oldWeight){
        resetWeight(newWeight, oldWeight);
    }
    
    public void resetWeight(int newWeight,int oldWeight){
        //少于等于0无效，默认为1
        this.newUserWeight = newWeight <= 0?1:newWeight;
        this.oldUserWeight = oldWeight <= 0?1:oldWeight;
        
        try {
            resetLock.writeLock().lock();
            queueItems.clear();
            queueItems.add(new QueueItem(TradeEntrust.EntrustTagOld, this.oldUserWeight));
            queueItems.add(new QueueItem(TradeEntrust.EntrustTagNew, this.newUserWeight));
            //计算相关权重
            this.totalQueue = queueItems.size();
            this.currentIndex = this.totalQueue - 1;
            this.maxWeight = Math.max(this.newUserWeight, this.oldUserWeight);
            this.gcdWeight = gcd(this.newUserWeight,this.oldUserWeight);
        } finally {
            resetLock.writeLock().unlock();
        }
    }
    
    @Override
    protected boolean isPriceFavorable(long price, long compare) {
        //忽略
        return false;
    }

    @Override
    protected PriceCell getInitLeaderCell() {
        return null;
    }

    @Override
    public void placeEntrust(TradeEntrust entrust) {
        if (entrust.getEntrustTag() == TradeEntrust.EntrustTagNew) {
            newUserQueue.placeEntrust(entrust);
        } else {
            oldUserQueue.placeEntrust(entrust);
        }
    }

    @Override
    public TradeEntrust getOldestEntrust() {
        //获取当前队列信息
        boolean isEmptyOfNewQueue = Boolean.FALSE;
        boolean isEmptyOfOldQueue = Boolean.FALSE;
        TradeEntrust entrust = null;
        while(true && (!isEmptyOfNewQueue || !isEmptyOfOldQueue)){
            QueueItem item = round();
            if (item.getIndex() == TradeEntrust.EntrustTagNew) {
                if (!isEmptyOfNewQueue) {
                    entrust = newUserQueue.getOldestEntrust();
                    isEmptyOfNewQueue = entrust == null?true:false;
                }
            } else if(item.getIndex() == TradeEntrust.EntrustTagOld) {
                if (!isEmptyOfOldQueue) {
                    entrust = oldUserQueue.getOldestEntrust();
                    isEmptyOfOldQueue = entrust == null?true:false;
                }
            } else {
               return null;
            }
            if (entrust != null) {
                return entrust;
            }
        }
        return null;
    }

    @Override
    public boolean removeEntrust(TradeEntrust entrust) {
        if (entrust.getEntrustTag() == TradeEntrust.EntrustTagNew) {
            return newUserQueue.removeEntrust(entrust);
        } else {
            return oldUserQueue.removeEntrust(entrust);
        }
    }
    
    @Override
    public PriceCell getHeader() {
        //获取当前队列信息
        boolean isEmptyOfNewQueue = Boolean.FALSE;
        boolean isEmptyOfOldQueue = Boolean.FALSE;
        PriceCell cell = null;
        while(true && (!isEmptyOfNewQueue || !isEmptyOfOldQueue)){
            QueueItem item = round();
            if (item.getIndex() == TradeEntrust.EntrustTagNew) {
                if (!isEmptyOfNewQueue) {
                    cell = newUserQueue.getHeader();
                    isEmptyOfNewQueue = cell == null?true:false;
                }
            } else if(item.getIndex() == TradeEntrust.EntrustTagOld) {
                if (!isEmptyOfOldQueue) {
                    cell = oldUserQueue.getHeader();
                    isEmptyOfOldQueue = cell == null?true:false;
                }
            } else {
               return null;
            }
            if (cell != null) {
                return cell;
            }
        }
        return null;
    }
    
    private class QueueItem{
        private int index;
        private int weight;
        
        public QueueItem(int index,int weight){
            this.index = index;
            this.weight = weight;
        }

        public int getIndex() {
            return index;
        }

        public int getWeight() {
            return weight;
        }
    }
    
    private QueueItem round() {  
        while (true) {
            try {
                resetLock.readLock().lock();
                currentIndex = (currentIndex + 1) % totalQueue;  
                if (currentIndex == 0) {  
                    currentWeight = currentWeight - gcdWeight;  
                    if (currentWeight <= 0) {  
                        currentWeight = maxWeight;  
                        if(currentWeight == 0) { 
                            return null;
                        }  
                    }  
                }  
                if(queueItems.get(currentIndex).getWeight() >= currentWeight) { 
                    return queueItems.get(currentIndex);  
                }
            } finally {
                resetLock.readLock().unlock();
            }
        }  
    }  
    
    /** 
     * 求两个数的最大公约数 4和6最大公约数是2 
     *  
     * @param num1 
     * @param num2 
     * @return 
     */  
    private int gcd(int num1, int num2) {  
        BigInteger i1 = new BigInteger(String.valueOf(num1));  
        BigInteger i2 = new BigInteger(String.valueOf(num2));  
        return i1.gcd(i2).intValue();  
    }  
    
    public static void main(String[] args) {
        final TimePriorityCompositeQueue test = new TimePriorityCompositeQueue(5,1);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                     e.printStackTrace();
                }
                System.out.println("更新权重....");
                test.resetWeight(4, 1);
            }
        }).start();
        for(int i=0;i<20;i++){
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                 e.printStackTrace();
            }
            test.getOldestEntrust();
        }
    }
    
    @Override
    public void visit(QueueVisitor visitor) {
       PriceCell newPriceCell =  newUserQueue.getHeader();
       if (newPriceCell != null) {
           newPriceCell.accept(visitor);
       }
       
       PriceCell oldPriceCell = oldUserQueue.getHeader();
       if (oldPriceCell != null) {
           oldPriceCell.accept(visitor);
       }
    }

    public int getNewUserWeight() {
        return newUserWeight;
    }

    public int getOldUserWeight() {
        return oldUserWeight;
    }

    public TimePriorityQueue getOldUserQueue() {
        return oldUserQueue;
    }

    public TimePriorityQueue getNewUserQueue() {
        return newUserQueue;
    }

}
